package com.djdevs.calculatorconverterfree;

import java.text.DecimalFormat;
import java.util.regex.*;

public class CalculatorParser {
	public CalculatorParser() { }
	
	private static final int MAX_LENGTH = 10;

	public static String FormatNumber(double number) {
		try {
	        int digitsAvailable = MAX_LENGTH - 2;
	        if (Math.abs(number) < Math.pow(10, digitsAvailable)
	                && Math.abs(number) > Math.pow(10, -digitsAvailable)) {
	            String format = "0.";
	            double temp = number;
	            for (int i = 0; i < digitsAvailable; i++) {
	                if ((temp /= 10) < 1) {
	                    format += "#";
	                }
	            }
	            return new DecimalFormat(format).format(number);
	        }
	        String format = "0.";
	        for (int i = 0; i < digitsAvailable; i++) {
	                format += "#";
	        }
	        String r = new DecimalFormat(format + "E0").format(number);
	        int lastLength = r.length() + 1;
	        while (r.length() > MAX_LENGTH && lastLength > r.length()) {
	            lastLength = r.length();
	            r = r.replaceAll("\\.?[0-9]E", "E");
	        }
	        return r;
		}
		catch (Exception e) {
			return "undefined";
		}
    }
	
	public static String CalculateBraces(String txtList, int openIndex)
    {
		try {
			int closeIndex = 0;
	    	String result = "";
	    	double dblResult = 0.0;
	    	String subTxtList = txtList.substring(openIndex + 1, txtList.length());
	    	String firstTxtList = txtList.substring(0, openIndex);
	    	
			if (subTxtList.indexOf("(") != -1) {
				if (subTxtList.indexOf("(") < subTxtList.indexOf(")")) {
					result = CalculateBraces(subTxtList, subTxtList.indexOf("("));
					firstTxtList = firstTxtList + result;
				}
	    		else {
	    			closeIndex = subTxtList.indexOf(")");
	    			dblResult = evaluate(subTxtList.substring(0, closeIndex));
	    			firstTxtList = firstTxtList + dblResult;
	    			
	    			result = CalculateBraces(subTxtList.substring(closeIndex + 1, subTxtList.length())
	    					, subTxtList.substring(closeIndex + 1, subTxtList.length()).indexOf("(")); 
	    			firstTxtList = firstTxtList + result;
	    			if (firstTxtList.indexOf("(") == -1) {
	    				firstTxtList = firstTxtList.replace(")", "");
	    			}
				}
			}
			else {
				closeIndex = subTxtList.indexOf(")");
				dblResult = evaluate(subTxtList.substring(0, closeIndex));
				firstTxtList = firstTxtList + dblResult + subTxtList.substring(closeIndex + 1, subTxtList.length());
				firstTxtList = firstTxtList.replace(")", "");
			}
			
			return firstTxtList;
		}
		catch (Exception e) {
			return "undefined";
		}
    	
    }
    
    public static boolean IfOperatorExists(String subTxtList)
    {
    	if (subTxtList.indexOf("√") != -1 || subTxtList.indexOf("±") != -1 || subTxtList.indexOf("%") != -1 
    			|| subTxtList.indexOf("/") != -1 || subTxtList.indexOf("*") != -1 || subTxtList.indexOf("+") != -1 
    			|| subTxtList.indexOf("-") != -1 || subTxtList.indexOf("^") != -1) {
			return true;
		}
    	return false;
    }

	public static Double evaluate(String expression) throws Exception {
		try {
			double dblResult  = 0;
			String result = expression;
			CalcExpression bodmas = new CalcExpression(result);
			while(bodmas.hasMatch()) {
				double firstNumber = bodmas.getLeft();
				char operator = bodmas.getOperator();
				double secondNumber = bodmas.getRight();
	
				if (operator == '^') {
					dblResult = Math.pow(firstNumber, secondNumber);
					result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
				}
				else if (operator == '/') {
					dblResult = firstNumber / secondNumber;
					result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
				}
				else if (operator == '*') {
					dblResult = firstNumber * secondNumber;
					result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
				}
				else if (operator == '+') {
					dblResult = firstNumber + secondNumber;
					if (Math.signum(dblResult) == -1.0) {
						result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
					}
					else {
						if (result.substring(0, bodmas.getStart()).length()  == 0) {
							result = dblResult + result.substring(bodmas.getEnd());
						}
						else {
							result = result.substring(0, bodmas.getStart()) + "+" + dblResult + result.substring(bodmas.getEnd());
						}
					}
				}
				else if (operator == '-') {
					dblResult = firstNumber - secondNumber;
					result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
				}
				else if (operator == '√') {
					dblResult = Math.sqrt(secondNumber);
					result = result.substring(0, bodmas.getStart()) + dblResult + result.substring(bodmas.getEnd());
				}
				
				bodmas = new CalcExpression(result);
			}
			return Double.parseDouble(result);
		}
		catch (Exception e)
		{
			throw new Exception();
		}
	}
}

class CalcExpression {
	private double l = 0.0;
	private char o;
	private double r = 0.0;
	private int s = 0;
	private int e = 0;
	private boolean f;

	public CalcExpression(String text) {
		this.f = getMatchFor(text, '^');
		if(!this.f)
			this.f = getSqRtFor(text, '√');
		if(!this.f)
			this.f = getMatchFor(text, '/');
		if(!this.f)
			this.f = getMatchFor(text, '*');
		if(!this.f)
			this.f = getMatchFor(text, '+');
		if(!this.f)
			this.f = getMatchFor(text, '-');	
	}

	private boolean getMatchFor(String text, char operator) {
		try
		{
			String regex = "(-?[\\d\\.]+([Ee][+-]?[\\d]+)?)(\\x)(-?[\\d\\.]+([Ee][+-]?[\\d]+)?)";
			//old one for reference - String regex = "(-?[\\d\\.]+)(\\x)(-?[\\d\\.]+)";
			Matcher matcher = Pattern.compile(regex.replace('x', operator)).matcher(text);
			if(matcher.find()) {
				this.l = Double.parseDouble(matcher.group(1));
				this.o = matcher.group(3).charAt(0);
				this.r = Double.parseDouble(matcher.group(4));
				this.s = matcher.start();
				this.e = matcher.end();
				return true;
			}
			return false;
		}
		catch (Exception e)
		{
			return false;
		}
	}
	
	private boolean getSqRtFor(String text, char operator) {
		try
		{
			String regex = "(\\x)(-?[\\d\\.]+([Ee][+-]?[\\d]+)?)";
			//old one for reference - String regex = "(\\x)(-?[\\d\\.]+)";
			Matcher matcher = Pattern.compile(regex.replace('x', operator)).matcher(text);
			if(matcher.find()) {
				//this.l = Double.parseDouble(matcher.group(1));
				this.o = matcher.group(1).charAt(0);
				this.r = Double.parseDouble(matcher.group(2));
				this.s = matcher.start();
				this.e = matcher.end();
				return true;
			}
			return false;
		}
		catch (Exception e)
		{
			return false;
		}
	}
	
	public double getLeft() {
		return l;
	}

	public char getOperator() {
		return o;
	}

	public double getRight() {
		return r;
	}

	public int getStart() {
		return s;
	}

	public int getEnd() {
		return e;
	}

	public void setF(boolean f) {
		this.f = f;
	}

	public boolean hasMatch() {
		return f;
	}
}